#include <stdio.h>
#include <malloc.h>
#include "atom.h"
#include "dtd.h"
#include "dtddefs.h"
#include "dtdtab.h"

static void BuildAtomTable();
static void BuildEleTable();

static DTDElement *eleTab[MAXATOM];

/*
 * Initialize the dtd information
 */
void
InitDTD()
{
    BuildAtomTable();
    BuildEleTable();
}

/*
 * Add the predefined atom strings to the atom table
 */
static void
BuildAtomTable()
{
    int i;

    for (i = 1; i < MAXELEMENT; i++) {
	GetAtom(atoms[i]);
    }
}

/*
 * Read in the elements and put the into a simple array indexed
 * by atom values.  Note that since there are many more atoms
 * than just the elements, this table is fairly sparse.
 *
 * Because we allow developers to modify the information in the
 * table, we will copy each element into malloc'ed memory so
 * it can be freed by the programmer.
 */
static void
BuildEleTable()
{
    int i;
    int ecount = sizeof(eTable) / sizeof(DTDElement);

    for (i = 0; i < ecount; i++) {
	/* We want the data in allocated memory, not static */
	DTDElement *new = (DTDElement *)malloc(sizeof(DTDElement));

	if (new == 0) {
	    fprintf(stderr, "Out of memory\n");
	    return;
	}
	*new = eTable[i];		/* Copy data */
	ChangeElementInfo(new->name, new);
    }
}

/*
 * This function allows you to change the element data associated
 * with a particular atom.  The old information is return by the
 * function and should be freed (or reused).
 */
DTDElement *
ChangeElementInfo(atom element, DTDElement *new)
{
    DTDElement *old = GetElementInfo(element);

    eleTab[element] = new;
    return (old);
}

/*
 * Add a new atom as a parent to this element.
 * Return the number of parents in the element.
 * Return 0 on error.
 */
int
AddElementParent(atom element, atom parent)
{
    int i;

    if (eleTab[element] == 0) {
	return (0);
    }
    for (i = 0; i < 40; i++) {
	if (eleTab[element]->parents[i] == 0) {
	    eleTab[element]->parents[i] = parent;
	    return (i + 1);
	}
    }
    fprintf(stderr, "Elements limited to 40 parent atoms");
    return (0);
}

/*
 * Add a new atom as a child to this element.
 * Return the number of child in the element.
 * Return 0 on error.
 */
int
AddElementChild(atom element, atom child)
{
    int i;

    if (eleTab[element] == 0) {
	return (0);
    }
    for (i = 0; i < 40; i++) {
	if (eleTab[element]->children[i] == 0) {
	    eleTab[element]->children[i] = child;
	    return (i + 1);
	}
    }
    fprintf(stderr, "Elements limited to 40 children atoms");
    return (0);
}

/*
 * Get a 0 terminated list of atoms that represent elements that
 * can be contained in the passed in element.
 */
atom *
GetElementChildren(atom element)
{
    if (eleTab[element] == 0) {
	return (0);
    }
    return (eleTab[element]->children);
}

/*
 * Get a 0 terminated list of atoms that represent elements
 * that can be parented by the passed in element.
 */
atom *
GetElementParents(atom element)
{
    if (eleTab[element] == 0) {
	return (0);
    }
    return (eleTab[element]->parents);
}

/*
 * Get the 0 terminated list of attributes contained in the
 * passed-in element.
 */
atom *
GetElementAttrList(atom element)
{
    if (eleTab[element] == 0) {
	return (0);
    }
    return (eleTab[element]->attrList);
}

/*
 * Get the full attribute structure for the passed-in attribute 
 * in the passed-in element.
 */
DTDAttribute *
GetElementAttrInfo(atom element, atom attr)
{
    DTDAttribute *ap;

    if (eleTab[element] == 0) {
	return (0);
    }
    for (ap = &(eleTab[element]->attrs[0]); ap->name; ap++) {
	if (ap->name == attr)
	    return (ap);
    }
    return (0);
}
    
/*
 * Get the 0 terminated list of required attributes contained in the
 * passed-in element.
 */
atom *
GetElementReqAttrs(atom element)
{
    if (eleTab[element] == 0) {
	return (0);
    }
    return (eleTab[element]->reqAttrs);
}

/*
 * Get the 0 terminated list of fixed attributes contained in the
 * passed-in element.
 */
atom *
GetElementFixedAttrs(atom element)
{
    if (eleTab[element] == 0) {
	return (0);
    }
    return (eleTab[element]->fixedAttrs);
}

/*
 * Get the 0 terminated list of implied attributes contained in the
 * passed-in element.
 */
atom *
GetElementImpliedAttrs(atom element)
{
    if (eleTab[element] == 0) {
	return (0);
    }
    return (eleTab[element]->impliedAttrs);
}

/*
 * Get the termination type for the passed in element.
 * We assume that all unknown elements are required to
 * be terminated so if we can't find the element information
 * we return E_TERM_MAND.
 */
int
GetElementTerminator(atom element)
{
    if (eleTab[element] == 0) {
	return (E_TERM_MAND);
    }
    return (eleTab[element]->flags & 0x7);
}

/*
 * Return a pointer to the full structure information on the
 * passed-in element.
 */
DTDElement *
GetElementInfo(atom element)
{
    if (eleTab[element] == 0) {
	return (0);
    }
    else {
	return (eleTab[element]);
    }
}

/*
 * Returns 1 if child element is a valid child of parent element.
 * 0 otherwise.
 */
int
ValidRelationship(atom parent, atom child)
{
    int i;
    DTDElement *ep;

    ep = eleTab[child];
    if (ep == 0) {
	return (1);		/* Allow unknowns to exist in anything */
    }
    ep = eleTab[parent];
    if (ep == 0) {
	return (0);
    }
    for (i = 0; i < 40; i++) {
	if (ep->children[i] == child) {
	    return (1);
	}
    }
    return (0);
}
